EntityDefinition   A
last analyzed

Complexity

Total Complexity 40

Size/Duplication

Total Lines 147
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 87
dl 0
loc 147
rs 9.2
c 0
b 0
f 0
wmc 40

18 Functions

Rating   Name   Duplication   Size   Complexity  
A getField 0 3 1
A getToManyAssociations 0 12 4
A getTranslatableFields 0 8 1
A isToManyAssociation 0 3 3
A filterProperties 0 18 3
A isJsonObjectField 0 3 1
A getToOneAssociations 0 12 4
A getEntity 0 3 1
A isJsonField 0 3 3
A isScalarField 0 3 3
A getRequiredFields 0 8 2
A getAssociationFields 0 8 1
A forEachField 0 8 2
A getPrimaryKeyFields 0 8 2
A isToOneAssociation 0 3 3
A isJsonListField 0 3 1
A isOneToOneAssociation 0 3 1
A isTranslatableField 0 3 2

How to fix   Complexity   

Complexity

Complex classes like EntityDefinition often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/**
2
 * @package admin
3
 */
4
5
/* @private */
6
export interface Property {
7
    flags?: {
8
        primary_key?: boolean,
9
        required?: boolean,
10
        translatable?: boolean,
11
    },
12
    required?: boolean,
13
    type?: string,
14
    relation?: 'one_to_one' | 'one_to_many' | 'many_to_one' | 'many_to_many',
15
    entity?: string,
16
}
17
18
interface Properties {
19
    [key: string]: Property;
20
}
21
22
const scalarTypes = ['uuid', 'int', 'text', 'password', 'float', 'string', 'blob', 'boolean', 'date'];
23
const jsonTypes = ['json_list', 'json_object'];
24
25
// eslint-disable-next-line sw-deprecation-rules/private-feature-declarations
26
export function getScalarTypes() {
27
    return scalarTypes;
28
}
29
30
// eslint-disable-next-line sw-deprecation-rules/private-feature-declarations
31
export function getJsonTypes() {
32
    return jsonTypes;
33
}
34
35
// eslint-disable-next-line sw-deprecation-rules/private-feature-declarations
36
export default class EntityDefinition<EntityName extends keyof EntitySchema.Entities> {
37
    readonly entity: EntitySchema.Entity<EntityName>;
38
39
    readonly properties: Properties;
40
41
    constructor({ entity, properties }: { entity: EntitySchema.Entity<EntityName>, properties: Properties }) {
42
        this.entity = entity;
43
        this.properties = properties;
44
    }
45
46
    getEntity() {
47
        return this.entity;
48
    }
49
50
    /**
51
     * returns an Object containing all primary key fields of the definition
52
     * @returns {Object}
53
     */
54
    getPrimaryKeyFields() {
55
        return this.filterProperties((property) => {
56
            return property.flags?.primary_key === true;
57
        });
58
    }
59
60
    /**
61
     * returns an Object containing all associations fields of this definition
62
     * @returns {Object}
63
     */
64
    getAssociationFields() {
65
        return this.filterProperties((property) => {
66
            return property.type === 'association';
67
        });
68
    }
69
70
    /**
71
     * returns all toMany associationFields
72
     * @returns {Object}
73
     */
74
    getToManyAssociations() {
75
        return this.filterProperties((property) => {
76
            if (property.type !== 'association') {
77
                return false;
78
            }
79
80
            return ['one_to_many', 'many_to_many'].includes(property.relation ?? '');
81
        });
82
    }
83
84
    /**
85
     * returns all toMany associationFields
86
     * @returns {Object}
87
     */
88
    getToOneAssociations() {
89
        return this.filterProperties((property) => {
90
            if (property.type !== 'association') {
91
                return false;
92
            }
93
94
            return ['one_to_one', 'many_to_one'].includes(property.relation ?? '');
95
        });
96
    }
97
98
    /**
99
     * returns all translatable fields
100
     * @returns {Object}
101
     */
102
    getTranslatableFields() {
103
        return this.filterProperties((property) => {
104
            return this.isTranslatableField(property);
105
        });
106
    }
107
108
    /**
109
     *
110
     * @returns {Object}
111
     */
112
    getRequiredFields() {
113
        return this.filterProperties((property) => {
114
            return property.flags?.required === true;
115
        });
116
    }
117
118
    /**
119
     * Filter field definitions by a given predicate
120
     * @param {Function} filter
121
     */
122
    filterProperties(filter: (property: Property) => boolean) {
123
        if (typeof filter !== 'function') {
124
            return {};
125
        }
126
127
        const result: Properties = {};
128
        Object.keys(this.properties).forEach((propertyName) => {
129
            if (filter(this.properties[propertyName])) {
130
                result[propertyName] = this.properties[propertyName];
131
            }
132
        });
133
134
        return result;
135
    }
136
137
    getField(name: string) {
138
        return this.properties[name];
139
    }
140
141
    forEachField(callback: (property: Property, propertyName: string, properties: Properties) => void) {
142
        if (typeof callback !== 'function') {
143
            return;
144
        }
145
146
        Object.keys(this.properties).forEach((propertyName) => {
147
            callback(this.properties[propertyName], propertyName, this.properties);
148
        });
149
    }
150
151
    isScalarField(field: Property) {
152
        return scalarTypes.includes(field.type ?? '');
153
    }
154
155
    isJsonField(field: Property) {
156
        return jsonTypes.includes(field.type ?? '');
157
    }
158
159
    isJsonObjectField(field: Property) {
160
        return field.type === 'json_object';
161
    }
162
163
    isJsonListField(field: Property) {
164
        return field.type === 'json_list';
165
    }
166
167
    isToManyAssociation(field: Property) {
168
        return field.type === 'association' && ['one_to_many', 'many_to_many'].includes(field.relation ?? '');
169
    }
170
171
    isToOneAssociation(field: Property) {
172
        return field.type === 'association' && ['many_to_one', 'one_to_one'].includes(field.relation ?? '');
173
    }
174
175
    isOneToOneAssociation(field: Property) {
176
        return field.type === 'association' && field.relation === 'one_to_one';
177
    }
178
179
    isTranslatableField(field: Property) {
180
        return (field.type === 'string' || field.type === 'text') && field.flags?.translatable === true;
181
    }
182
}
183